package wmarduino;

import Structures.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Random;
import java.util.Scanner;

/**
 * Clase principal que pone en marcha el hilo de ejecución de la aplicación,
 * para ejecutar los métodos necesarios para dar interfaz al usuario.
 *
 * @author Alexandre
 * @author Rodrigo
 * @author Gabriel
 * @author Jeremy
 */
public class WMArduino {

    private static Domain wmaGraph = new Domain(); //Referencia al grado(Dominio) utilizado.
    private static Random randomGenerator = new Random(); //Inicializa un generador de números aleatorios.

    /**
     * Metodo que despliega las distintas opciones que pueda utilizar el usuario
     * para diseñar el grafo.
     *
     */
    private static void menu() {
        try {
            int opc;
            System.out.println("  Menu ");
            System.out.println(" 1) Add Node");
            System.out.println(" 2) Add Conection");
            System.out.println(" 3) Delete Node");
            System.out.println(" 4) Delete Conection");
            System.out.println(" 5) Print Graph");
            System.out.println(" 6) Bounty Hunter");
            System.out.println(" 7) Find Node");
            System.out.println(" 8) Arduino");
            System.out.print(" Select an option: ");
            opc = Integer.parseInt(verifyExitProgram());
            printSout();
            switch (opc) {
                case 1:
                    System.out.println(" Add Node");
                    System.out.println("");
                    addNodeGrapgh();
                    break;
                case 2:
                    System.out.println(" Add Conection");
                    System.out.println("");
                    addConetionGrapgh();
                    break;
                case 3:
                    System.out.println(" Delete Node");
                    System.out.println("");
                    deleteNodeGraph();
                    break;
                case 4:
                    System.out.println(" Delete Conection");
                    System.out.println("");
                    deleteConectionGraph();
                    break;
                case 5:
                    System.out.println(" Print Graph");
                    System.out.println("");
                    printGraph();
                    printSout();
                    menu();
                    break;
                case 6:
                    System.out.println(" Bounty Hunter");
                    System.out.println("");
                    menuBountyHunter();
                    createBountyHunters();
                    printSout();
                    menu();
                    break;
                case 7:
                    System.out.println(" Find Node");
                    System.out.println("");
                    findnode();
                    printSout();
                case 8:
                    System.out.println("Arduino");
                    System.out.println("");
                    createArduino();
                    printSout();
                default:
                    System.out.println(" The option you seleted is not valid.1");
                    printSout();
                    menu();
                    break;
            }

        } catch (NumberFormatException | SocketException e) {
            System.out.println(" The option you seleted is not valid.2");
            printSout();
            menu();
        }
    }

    /**
     * Metodo que genera grafo aleatoriamente.
     *
     */
    private static void implementGraph() throws SocketException {
        int rdmAmount = 5;
        //int rdmAmount = randomGenerator.nextInt(999);
        String format = String.format("%%0%dd", 3);
        String myIP = myIPAdress();
        wmaGraph.setIdentificator(myIP);
        String strId;
        String nodeId;

        //System.out.println("Identificador: " + myIP);

        for (int intId = 0; intId < rdmAmount; intId++) {
            strId = String.format(format, intId); // "001"            
            nodeId = myIP.concat(":" + strId);  // "192.168.01.05:001"
            // Metodo que agrega al grafo.
            wmaGraph.addNode(nodeId);
        }

        if (wmaGraph.getsize() >= 1) {
            int n = wmaGraph.getsize();
            int edgeCount = 12;
            //int edgeCount = randomGenerator.nextInt((n - 1) * n);//Maximo de aristas que se pueden crear

            for (int i = 1; i <= edgeCount; i++) {
                int intNSource = randomGenerator.nextInt(n - 1);
                int intNDest = randomGenerator.nextInt(n - 1);
                int intWeight = randomGenerator.nextInt(10);

                Node nodeSource = wmaGraph.findNode(intNSource);
                Node nodeDest = wmaGraph.findNode(intNDest);
                if (!wmaGraph.containEdge(nodeSource, nodeDest)) {
                    if(intWeight != 0){
                    wmaGraph.addEdge(nodeSource, nodeDest, intWeight);
                    }else{
                        i--;
                    }
                } else {
                    i--;
                }
            }
        }
    }

    /**
     * Metodo para obtener la ip del ordenador.
     *
     * @return myIP Retorna la ip interna del ordenador.
     * @throws SocketException
     */
    public static String myIPAdress() throws SocketException {
//      System.out.println("Host addr: " + InetAddress.getLocalHost().getHostAddress());  // Windows
        Enumeration<NetworkInterface> n = NetworkInterface.getNetworkInterfaces();
        InetAddress addr1 = null;
        InetAddress addr2 = null;
        String myIP = "Uknown";
        for (; n.hasMoreElements();) {
            NetworkInterface e = n.nextElement();
            //System.out.println("Interface: " + e.getName());
            Enumeration<InetAddress> a = e.getInetAddresses();

            for (; a.hasMoreElements();) {
                if (addr1 == null) {
                    addr1 = a.nextElement();
                } else {
                    addr2 = a.nextElement();
                }
            }
            myIP= addr2.getHostAddress(); 
           // System.out.println("  " + myIP);
            break;
        }
        return myIP;
    }

    /**
     * Metodo para espaciar entre lineas del menu que se presenta al usuario.
     *
     */
    private static void printSout() {
        System.out.println("");
        System.out.println("********************************************************");
        System.out.println("");
    }

    /**
     *
     * Metodo que verifica el scanner para ver si es regresar al menu principal
     * o finalizar la ejecucion del programa.
     *
     * @return Un string del scanner obtenido despues de verificar si es salida
     * del progroma o regresar menu principal.
     *
     */
    public static String verifyExitProgram() {
        Scanner sc = new Scanner(System.in);
        String cod = sc.next();
        if (cod.equals("Exit") || cod.equals("exit")) {
            System.exit(0);
        } else if (cod.equals("Menu") || cod.equals("menu")) {
            printSout();
            menu();
        } else if (cod.equals("")) {
            verifyExitProgram();
        } else {
            return cod;
        }
        return cod;
    }

    /**
     * Imprime en consola el grafo completo, primero la lista de nodos en el
     * mismo (identificadores de cada nodo), y luego todas las conexiones en el
     * siguiente orden cada una: nodo fuente, peso, nodo destino.
     */
    public static void printGraph() {
        System.out.println(" NODES:");
        wmaGraph.printNodes();
        System.out.println("");
        System.out.println("\n" + " CONNECTIONS: ");
        wmaGraph.conectionsTour();
    }

    /**
     *
     * @param args
     * @throws SocketException
     */
    public static void main(String[] args) throws SocketException {

        System.out.println(" Generating Graph... ");
        implementGraph();
        System.out.println(" Graph Generated Succefully! ");
        System.out.println("");
        System.out.println(" Your Started Graph: ");
        printGraph();
        printSout();
        menu();
    }

    /**
     * Se encarga de mostrar el menu en consola para que el usuario pueda añadir
     * los nodos especiales, nodo servidor y nodo cliente.
     */
    private static void addNodeMenu() {
        int opc;
        System.out.println(" Select type of node.");
        System.out.println(" 1) Add Normal Node");
        System.out.println(" 2) Add Client Node");
        System.out.println(" 3) Add Server Node");
        try {
            opc = Integer.getInteger(verifyExitProgram());

            switch (opc) {
                case 1:
                    printSout();
                    addNodeGrapgh();
                    break;
                case 2:
                    printSout();
                    addNodeMenu();
                    break;
                case 3:
                    printSout();
                    addNodeMenu();
                    break;
            }

        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.3");
            printSout();
            addNodeMenu();
        }
    }

    
 //-----------------------------  Bounty Hunters Methods   --------------------------------------------------//
        
    private static void menuBountyHunter(){
        int opc;
        System.out.println(" Menu Bounty Hunters ");
        System.out.println(" 1) Create Bounty Hunnter.");
        System.out.println(" 2) Visit a Specified Node.");
        System.out.println(" 3) Call the Bounty Hunters to Come Back Home.");
        System.out.println(" 4) Get Points of all the Bounty Hunters.");
        System.out.println(" 5) Print Bounty Hunters List Home.");
        System.out.println(" 6) Print Bounty Hunters List Away.");
        System.out.println(" 7) Send Bounty Hunters to Hunt.");
        System.out.print(" Selet an Option: ");
        opc = Integer.parseInt(verifyExitProgram());
        try{
            switch (opc) {
                case 1:                    
                    printSout();
                    createBountyHunters();
                    printSout();
                    menuBountyHunter();
                    break;
                case 2:
                    printSout();
                    System.out.println(" Visit a Specify Node");
                    visitNodeBountyHunter();
                    printSout();
                    menuBountyHunter();
                    break;
                case 3:
                    printSout();
                    System.out.println(" Bring Home all Bounty Hunters Away");
                    printSout();
                    menuBountyHunter();
                    break;
                case 4:
                    printSout();
                    System.out.println(" Get all points of the Bounty Hunters.");
                    pointsBountyHunters();
                    printSout();
                    menuBountyHunter();
                    break;
                case 5:
                    printSout();
                    System.out.println(" Bounty Hunter in Home Domain.");
                    printlstHomeBH();
                    printSout();
                    menuBountyHunter();
                    break;
                case 6:
                    printSout();
                    System.out.println(" Bounty Hunters Away of Home Domain.");
                    printlstAwayBH(); 
                    printSout();
                    menuBountyHunter();
                    break;
                case 7:
                    printSout();
                    System.out.println("Bounty Hunters! Start the Hunt!!!");
                    System.out.println("");
                    workBountyHunters();
                    printSout();
                    menuBountyHunter();
                    break;
                default:
                    printSout();
                    menuBountyHunter();
                    break;
            }
        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.");
            printSout();
            menuBountyHunter();
        }

    }
    
    /**
     * 
     */
    public static void printlstHomeBH(){
        SLL lstHomeBH = wmaGraph.getDomainBountyHounters();
        BountyHunter tmpBH;
        String CID;
        String HD;
        for (SLLNode tmp = lstHomeBH.getFirst(); tmp != null; tmp = tmp.getSucc()){
            tmpBH = (BountyHunter) tmp.getElement();
            CID = tmpBH.getCID();
            HD = tmpBH.getHD();
            System.out.println(" Bounty Hunter ID: [" + HD + ":" + CID + "]");
        }        
    }
    
    /**
     * 
     */
    public static void printlstAwayBH() {
        SLL lstAwayBH = wmaGraph.getTravelBountyHounters();
        BountyHunter tmpBH;
        String CID;
        String HD;
        for (SLLNode tmp = lstAwayBH.getFirst(); tmp != null; tmp = tmp.getSucc()) {
            tmpBH = (BountyHunter) tmp.getElement();
            CID = tmpBH.getCID();
            HD = tmpBH.getHD();
            System.out.println(" Bounty Hunter ID: [" + HD + ":" + CID + "]");
        }
    }
       
    
    /**
     * 
     */
    private static void homeBountyHunter(){

    }
    
    /**
     * Metodo que regresa todos los puntos obtenidos de cada uno de los bounty hunters.
     * 
     */
    private static void pointsBountyHunters() {
        if (wmaGraph.getTravelBountyHounters().getSize() == 0) {
            SLL listBH = wmaGraph.getDomainBountyHounters();
            int totalPoints = 0;
            totalPoints = wmaGraph.getPoints();
            System.out.println(" Total of points obtain By the Bounty Hunters: " + totalPoints);
        } else {
            System.out.println(" There is still Bounty Hunters away of Home Domain.");
        }
        menuBountyHunter();
    }


    /**
     * 
     */
    private static void visitNodeBountyHunter(){
        String opc;
        String strIdent;
        int ident;
        int CID;
        SLLNode tmp;
        BountyHunter tmpBH = null;
        SLL listHBH = wmaGraph.getDomainBountyHounters();
        System.out.println(" Which Bounty Hunter to Command?");
        opc = verifyExitProgram();
        ident = Integer.parseInt(opc);
        strIdent = changeFormat(ident);

        for (tmp = listHBH.getFirst(); tmp != null; tmp = tmp.getSucc()) {
            tmpBH = (BountyHunter) tmp.getElement();
            if (tmpBH.getCID() == strIdent) {
                break;
            }
        }
        if (tmp == null) {
            System.out.println(" Bounty Hunter Not Found.");
        } else {
            Node node = new Node(ident);
            tmpBH.setTR(node);
        }
    }
    
    /**
     * Creacion de bounty Hunter; de acuerdo a la cantidad elegida por el
     * usuario.
     */

    private static void createBountyHunters() {
        String opc;
        int comp;
        String format = String.format("%%0%dd", 3);
        String strId;
        System.out.print(" How Many bountyHunter do you want created?: ");
        opc = verifyExitProgram();
        comp = Integer.parseInt(opc);
        
        try {
            Node primerNodoGrafo = wmaGraph.getFirstNode();
            while (comp > 0) {
                System.out.println("entra while");
                strId = String.format(format, wmaGraph.getcountBH());
                
                BountyHunter bountytemp = new BountyHunter(wmaGraph.getIdentificator(), primerNodoGrafo, strId, primerNodoGrafo, wmaGraph);
                (wmaGraph.getDomainBountyHounters()).insert(bountytemp);
                primerNodoGrafo = primerNodoGrafo.getNextNode();
                wmaGraph.setcountBH();
                comp--;       
                //bountytemp.start();
                
            }

        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.");
            printSout();
            createBountyHunters();
        }
//        SLLNode tmpN = wmaGraph.getDomainBountyHounters().getFirst();
//        while (tmpN != null) {
//            BountyHunter tmpB = (BountyHunter) tmpN.getElement();
//            tmpB.run();
//        }

        printSout();
        menu();
    }
    
    
    private static void workBountyHunters(){
        SLLNode temp=wmaGraph.getDomainBountyHounters().getFirst();
        while (temp!=null){
           BountyHunter tempBH= (BountyHunter)temp.getElement();
           tempBH.start();
        }
        
    }
    
    
     private static void createArduino() {
        String opc;
        int comp;
        System.out.print(" How Many Arduino do you want created?: ");
        opc = verifyExitProgram();
        comp = Integer.parseInt(opc);
        
        
            while (comp > 0) { 
                System.out.println("creando aruinos");
                Arduino a = new Arduino();
                
                Domain.getLtsArduinos().insert(a);
                
                comp--;       
                
            }
            
            SLLNode tmpArd = Domain.getLtsArduinos().getFirst();
            while (tmpArd != null) {
                System.out.println("asignando arduinos");
                
                SLLNode tmpBounty = wmaGraph.getDomainBountyHounters().getFirst();
                
                while (tmpBounty != null) {
                    System.out.println("Asig ard a bounty");
                    
                    BountyHunter bount = (BountyHunter) tmpBounty.getElement();
                    System.out.println(bount.getCID());
                    bount.getLtswhrASonVisitar().insert(tmpArd);
                    
                    tmpBounty = tmpBounty.getSucc();
                }
                
                tmpArd = tmpArd.getSucc();
            }
            
            

        
//        SLLNode tmpN = wmaGraph.getDomainBountyHounters().getFirst();
//        while (tmpN != null) {
//            BountyHunter tmpB = (BountyHunter) tmpN.getElement();
//            tmpB.run();
//        }

        printSout();
        menu();
    }

    //----------------------------------------------------------------------------------------//
    /**
     * Metodo que agrega una cantidad de nodos definida por el usuario.
     */
    private static void addNodeGrapgh() throws SocketException {
        String opc;
        int comp;
        String format = String.format("%%0%dd", 3);
        String myIP = myIPAdress();
        String strId;
        String nodeId;
        System.out.print(" How Many Nodes to Insert?: ");
        opc = verifyExitProgram();
        comp = Integer.parseInt(opc) - 1;
        try {
            if (opc.equals("back")) {
                printSout();
                menu();
            } else {
                while (comp >= 0) {
                    strId = String.format(format, wmaGraph.getsize()); // "001"            
                    nodeId = myIP.concat(":" + strId);  // "192.168.01.05:001"
                    // Metodo que agrega al grafo.
                    wmaGraph.addNode(nodeId);
                    comp--;
                }
            }
        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.4");
            printSout();
            addNodeGrapgh();
        }
        printSout();
        menu();
    }

    /**
     * Metodo que agrega una nueva coneccion definida por el usuario.
     */
    private static void addConetionGrapgh() {
        String con1, con2, wght;
        int intcon1, intcon2, intwght;
        Node ndcon1, ndcon2;
        try {
            System.out.print(" From which node(0 to n)?: ");
            con1 = verifyExitProgram();
            System.out.print(" To which node(0 to n)?: ");
            con2 = verifyExitProgram();
            System.out.print(" The weight(1 to 10) of the conection?: ");
            wght = verifyExitProgram();
            intcon1 = Integer.parseInt(con1);
            intcon2 = Integer.parseInt(con2);
            intwght = Integer.parseInt(wght);

            while (intwght > 10 || intwght == 0) {
                System.out.print(" The weight(1 to 10) of the conection?: ");
                wght = verifyExitProgram();
                intwght = Integer.parseInt(wght);
            }


            if (con1.equals("") || con2.equals("") || wght.equals("")) {
                System.out.println("");
                printSout();
                menu();
            } else {
                ndcon1 = wmaGraph.findNode(intcon1);
                ndcon2 = wmaGraph.findNode(intcon2);
                if (ndcon1 == null) {
                    System.out.println(" Doesn't exist node source,");
                    printSout();
                    addConetionGrapgh();
                } else if (ndcon2 == null) {
                    System.out.println(" Doesn't exist node destiny");
                    printSout();
                    addConetionGrapgh();
                } else {
                    wmaGraph.addEdge(ndcon1, ndcon2, intwght);
                    printSout();
                    menu();
                }

            }
        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.5");
            printSout();
            addConetionGrapgh();
        }
    }

    /**
     * Metodo que borra un nodo especifico defenido por el usuario. Apartir de
     * la position desde 1 hasta N (Numero de nodos).
     */
    public static void deleteNodeGraph() {
        String opc;
        Node nodeDlte;
        System.out.print(" In that position the node to be deleted is? ");
        opc = verifyExitProgram();
        nodeDlte = wmaGraph.findNode(Integer.parseInt(opc));
        if (nodeDlte != null) {
            wmaGraph.removeNode(nodeDlte);
            printSout();
            menu();
        } else {
            System.out.println(" Node not found.");
            printSout();
            menu();
        }
    }

    /**
     * Metedo que borra la coneccion entre dos nodos que el usuario escoje.
     *
     */
    public static void deleteConectionGraph() {
        String con1, con2;
        int intcon1, intcon2;
        Node ndcon1, ndcon2;
        boolean rmvEdge;

        System.out.print(" From which node?0 to n): ");
        con1 = verifyExitProgram();
        System.out.print(" To which node?(0 to n): ");
        con2 = verifyExitProgram();


        try {
            if (con1.equals("back") || con2.equals("back")) {
                printSout();
                menu();
            } else {
                intcon1 = Integer.parseInt(con1);
                intcon2 = Integer.parseInt(con2);
                ndcon1 = wmaGraph.findNode(intcon1);
                ndcon2 = wmaGraph.findNode(intcon2);
                if (ndcon1 == null) {
                    System.out.println(" Doesn't exist node source,");
                    printSout();
                    deleteConectionGraph();

                } else if (ndcon2 == null) {
                    System.out.println(" Doesn't exist node destiny");
                    printSout();
                    deleteConectionGraph();
                } else {
                    rmvEdge = wmaGraph.removedEdgeGraph(ndcon1, ndcon2);
                    if (rmvEdge) {
                        System.out.println(" Conection Deleted.");
                        printSout();
                        menu();
                    } else {
                        System.out.println(" Conection does not exist.");
                        printSout();
                        menu();
                    }
                }

            }
        } catch (Exception e) {
            System.out.println(" The option you seleted is not valid.7");
            printSout();
            deleteConectionGraph();
        }
    }

    /**
     * Metodo que busca un nodo especifico que necesita el usuario.
     */
    public static void findnode() {
        String opc;
        int intData;
        Node node;
        try {
            System.out.print(" Which Node do you wish to find?(0 - n): ");
            intData = Integer.parseInt(verifyExitProgram());
            node = wmaGraph.findNode(intData);
            System.out.println(node.getElement());
            findnode();
        } catch (Exception e) {
            System.out.println(" Node not found.");
            printSout();
            findnode();
        }
    }
    
    
    /**
     * Metodo que cambia el formato de un numero entero a string. 
     * @param pIdent Entra un numero entero. Ejemplo: 5.
     * @return Devuelve un string de formato identificador. Ejemeplo 005.
     */
    public static String changeFormat(int pIdent){
        String format = String.format("%%0%dd", 3);
        String result = String.format(format, pIdent);    
        return result;
    }
}
/*
 * Inilizia el Grafo y el Caza Recompenzas.
 */
//System.out.println(parts[1].trim()); 
//Recuperar el identificador del nodo.
//        String str = "192.168.01.15:021";
//        String[] str2;
//        str2 = str.split(":");
//        for (int i = 0; i < str2.length; i++)
//            System.out.println(str2[i]);
//        
//        
//        // Formato de tres digitos el identificador de los nodos
//        String format = String.format("%%0%dd", 3);
//        System.out.println(format);
//        String result = String.format(format, 21);
//        System.out.println(result);
